
#include "core/olisp_mem.h"
#include "utility/olisp_quote_mgr.h"

#ifdef LISP_FEAT_QUOTE_SUPPORT

static void dispose( lisp_quote_mgr_t * self );
static void set_quote_marker(
    lisp_quote_mgr_t * self, obj_t * elem, quote_type_t quote_marker );
static quote_type_t get_quote_marker(
    lisp_quote_mgr_t * self, obj_t * elem );

static lisp_quote_mgr_vtbl_t quote_mgr_vtbl =
{
    &dispose,
    &set_quote_marker,
    &get_quote_marker
};

typedef struct
{
    obj_t *   elem;
    quote_type_t    marker;
} quoted_pair;

typedef struct
{
    lisp_quote_mgr_vtbl_t * vtbl;

    #define QUOTED_BUF_GROW_SIZE 256
    quoted_pair *   pairs;
    size_t          length;
    size_t          allocated;
} quote_mgr_impl_t;

static void dispose( lisp_quote_mgr_t * self )
{
    quote_mgr_impl_t * s = (quote_mgr_impl_t *)self;

    if( s->pairs )
        lisp_free( s->pairs );
    lisp_free( s );
}


static void set_quote_marker(
    lisp_quote_mgr_t * self, obj_t * elem, quote_type_t quote_marker )
{
    quote_mgr_impl_t * s = (quote_mgr_impl_t *)self;
    size_t i;
    quoted_pair * pair = 0;

    // only lists and identifiers could be quoted
    if( (elem->vtbl->element_type != LISP_ELEM_ID) &&
        (elem->vtbl->element_type != LISP_ELEM_LIST)
        )
    {
        return;
    }

    // find quoted elem if any
    for( i = 0; i < s->length; ++ i )
    {
        if( s->pairs[i].elem == elem )
        {
            pair = s->pairs + i;
            break;
        }
    }

    if( !quote_marker )
    {
        // free quoted element
        if( pair )
            pair->elem = 0;
    }
    else
    {
        // do mark
        if( pair )
        {
            pair->marker = quote_marker;
        }
        else
        {
            // add new quoted elem
            size_t new_len = s->length + 1;
            if( new_len > s->allocated )
            {
                size_t new_alloc = s->allocated + QUOTED_BUF_GROW_SIZE;
                quoted_pair * pairs = lisp_realloc( s->pairs, new_alloc );
                if( pairs == 0 )
                {
                    // TODO: handle error
                    return;
                }

                s->pairs = pairs;
                s->allocated = new_alloc;
            }

            pair = s->pairs + s->length;
            s->length = new_len;

            pair->elem = elem;
            pair->marker = quote_marker;
        }
    }
}

static quote_type_t get_quote_marker(
    lisp_quote_mgr_t * self, obj_t * elem )
{
    quote_mgr_impl_t * s = (quote_mgr_impl_t *)self;
    size_t i;

    // find quoted elem if any
    for( i = 0; i < s->length; ++ i )
    {
        if( s->pairs[i].elem == elem )
        {
            return s->pairs[i].marker;
        }
    }

    return 0;
}


lisp_quote_mgr_t * create_quote_mgr()
{
    quote_mgr_impl_t * r = lisp_mallocz( sizeof(quote_mgr_impl_t) );
    if( r )
    {
        r->vtbl = &quote_mgr_vtbl;

        return (lisp_quote_mgr_t *)r;
    }
    return 0;
}

#endif // LISP_FEAT_QUOTE_SUPPORT
